﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using modele.interfaces;
using modele.impl;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;
using System.Windows.Shapes;

namespace UI
{
    [Serializable]
    /**
     * \file GestionTour.cs
     * \brief Classe gérant le déroulement des tours
     * \author Alexandre Carteron et Charles Robin
     * \version 1.0
     * \date 13/12/2012
     * 
     * Classe gérant le déroulement des tours
     */
    public class GestionTour
    {
        private Jeu jeu; /*!<Permet d'accéder directement au modèle*/
        [NonSerialized]private MainWindow mainWindow; /*!<Permet d'accéder à la fenêtre principale*/
        public int tailleMap { get; set; }/*!<Entier représentant la taille de la carte*/
        private int nbtour;/*!<Entier représentant le nombre de tours écoulés*/
        [NonSerialized]
        private Rectangle[,] couleurCase;/*!<tableau de références vers les objets graphiques à mettre à jour pour représenter la possession des cases*/
        public bool[,] brouillard { get; set; }/*!<Entier représentant la taille de la map */

        /**
         * \fn public GestionTour(int tailleCarte)
         * \brief Constructeur de la classe GestionTour
         * 
         * Constructeur de la classe GestionTour
         * 
         * \param[in] taille entier représentant la taille du coté de la carte
         *
         */
        public GestionTour(int tailleCarte)
        {
            mainWindow = UI.MainWindow.INSTANCE;
            tailleMap = tailleCarte;
            couleurCase = new Rectangle[tailleCarte, tailleCarte];
            for (int i = 0; i < tailleCarte; i++)
                for (int j = 0; j < tailleCarte; j++)
                    initCouleurCase(i, j);
            brouillard = new bool[tailleCarte, tailleCarte];
            jeu = JeuImpl.INSTANCE;
            mainWindow.JoueurCourant.Content = "Joueur : " + jeu.joueurs[jeu.joueurCourant].getNom();
            mainWindow.nbTour.Content = "Tour : " + nbtour;
            nbtour = 0;
        }

        /**
         * \fn public void load()
         * \brief Met à jour la vue lors du chargement d'une carte
         * 
         * Met à jour la vue lors du chargement d'une carte
         * 
         *
         */
        public void load()
        {
            mainWindow = MainWindow.INSTANCE;
            jeu = MainWindow.INSTANCE.jeu;
            couleurCase = new Rectangle[tailleMap, tailleMap];
            for (int i = 0; i < tailleMap; i++)
                for (int j = 0; j < tailleMap; j++)
                    initCouleurCase(i, j);
            mettreAJourCarte();
            mainWindow = UI.MainWindow.INSTANCE;
            mainWindow.JoueurCourant.Content = "Joueur : " + jeu.joueurs[jeu.joueurCourant].getNom();
            mainWindow.nbTour.Content = "Tour : " + nbtour;
            foreach (Joueur j in jeu.joueurs)
                foreach (Ville v in j.villes)
                {
                    creerVille(v.x, v.y);
                }
        }

        /**
         * \fn public void finTour()
         * \brief Gère le passage de tour d'un joueur à l'autre
         * 
         * Gère le passage de tour d'un joueur à l'autre
         * 
         *
         */
        public void finTour()
        {
            FabriqueCarteVue.montrer = false;
            MainWindow.INSTANCE.cacherProp();
            Joueur j = jeu.joueurs[jeu.joueurCourant];
            //utile si y'a du redebugging à faire sur les conditions de défaite
            //Console.WriteLine("Unités : " + j.unites.Count + ", Civilisé : " + j.civilisé + ", Villes : " + j.villes.Count);
            if ((j.unites.Count == 0 && !j.civilisé) || (j.civilisé && j.villes.Count == 0))
            {
                jeu.joueurs.Remove(j);
                //Console.WriteLine(j.getNom() + " a perdu !");
                MessageBoxResult defaite = MessageBox.Show(MainWindow.INSTANCE, "La civilisation est éphémère, et " + j.getNom() + " en a fait les frais ! \nSon nom aura tôt fait d'être oublié dans les limbes de l'histoire ...", "Défaite du joueur " +j.getNom() + " !");
                MessageBoxResult victoire;
                if (jeu.joueurs.Count == 1)
                     victoire = MessageBox.Show(MainWindow.INSTANCE, "Ouech mec, bien ouej, t'as gnéga !", "Victoire du joueur " +jeu.joueurs[0].getNom() + " !");
            }
            else 
            {
                foreach (UniteImpl u in jeu.joueurs[jeu.joueurCourant].unites)
                {
                    if (u.mouvement == u.getMouvementMax() && u.vie < u.getVieMax())
                    {
                        u.vie++;
                    }
                    u.mouvement = u.getMouvementMax();
                }

                miseAJourVilles();
            }
            
            nbtour += (jeu.joueurCourant + 1) / jeu.joueurs.Count;
            jeu.joueurCourant = (jeu.joueurCourant + 1) % jeu.joueurs.Count;
            mainWindow.JoueurCourant.Content = ("Joueur : " + jeu.joueurs[jeu.joueurCourant].getNom());
            mainWindow.nbTour.Content = "Tour : "+nbtour ;
            mettreAJourCarte();
            FabriqueCarteVue.removeSelection();
            Grid.SetColumn(MainWindow.INSTANCE.selectionRectangle, jeu.joueurs[jeu.joueurCourant].curseurX);
            Grid.SetRow(MainWindow.INSTANCE.selectionRectangle, 1);
            FabriqueCarteVue.lignes[jeu.joueurs[jeu.joueurCourant].curseurY].Children.Add(MainWindow.INSTANCE.selectionRectangle);
            MainWindow.INSTANCE.selectionRectangle.Visibility = Visibility.Visible;
            TabManager.makeDocks();
        }

        /**
         * \fn public void miseAJourVilles()
         * \brief Gère l'aggrandissement et la production des villes à chaque tour
         * 
         * Gère l'aggrandissement et la production des villes à chaque tour
         * 
         *
         */
        public void miseAJourVilles()
        {
            foreach (Ville v in jeu.joueurs[jeu.joueurCourant].villes)
                if (v.miseAJour())
                {
                    int x;
                    int y;
                    int rang;
                    List<int> xs = new List<int>();
                    List<int> ys = new List<int>();
                    for (int i = -1; i < 2; i++)
                        for (int j = -1; j < 2; j++)
                            if (v.x + i >= 0 && v.x + i < tailleMap && v.y + j >= 0 && v.y + j < tailleMap && MainWindow.INSTANCE.jeu.carte.getInfosCase(v.x + i, v.y + j).dependanceVille == null)
                            {
                                xs.Add(v.x + i);
                                ys.Add(v.y + j);
                            }
                    if(xs.Count == 0)
                        for (int i = -2; i < 3; i++)
                            for (int j = -2; j < 3; j++)
                                if (v.x + i >= 0 && v.x + i < tailleMap && v.y + j >= 0 && v.y + j < tailleMap && MainWindow.INSTANCE.jeu.carte.getInfosCase(v.x + i, v.y + j).dependanceVille == null)
                                {
                                    xs.Add(v.x + i);
                                    ys.Add(v.y + j);
                                }

                    if (xs.Count != 0)
                    {
                        rang = (new Random()).Next(xs.Count);
                        x = xs[rang];
                        y = ys[rang];
                        v.expand(MainWindow.INSTANCE.jeu.carte.getCase(x, y));
                        MainWindow.INSTANCE.jeu.carte.getInfosCase(x, y).dependanceVille = v;
                        //initCouleurCase(x, y);
                        v.population++;
                    }
                    
                }
        }
        /**
         * \fn public void mettreAJourCarte()
         * \brief Actualise l'affichage de la carte
         * 
         * Actualise le brouillard, l'affichage des villes, des unités et les propositions éventuelles de positionnement de ville
         * 
         *
         */
        public void mettreAJourCarte()
        {
            for (int i = 0; i < tailleMap; i++)
                for (int j = 0; j < tailleMap; j++)
                    brouillard[i, j] = true;
            foreach (Unite u in jeu.joueurs[jeu.joueurCourant].unites)
            {
                for (int i = -2; i < 3; i++)
                    for (int j = -2; j < 3; j++)
                        if (i*i + j*j < 6 && i + u.x >= 0 && i + u.x < tailleMap && j + u.y >= 0 && j + u.y < tailleMap)
                        {
                            brouillard[i + u.x, j + u.y] = false;
                        }
            }
            foreach (Ville v in jeu.joueurs[jeu.joueurCourant].villes)
            {
                for (int i = -3; i < 4; i++)
                    for (int j = -3; j < 4; j++)
                        if (i * i + j * j < 12 && i + v.x >= 0 && i + v.x < tailleMap && j + v.y >= 0 && j + v.y < tailleMap)
                        {
                            brouillard[i + v.x, j + v.y] = false;
                        }
            }
            for (int i = 0 ; i < jeu.carte.taille ; i++)
                for (int j = 0; j < jeu.carte.taille; j++)
                {
                    mettreAJourCase(i, j);
                }
            int x = jeu.joueurs[jeu.joueurCourant].curseurX;
            int y = jeu.joueurs[jeu.joueurCourant].curseurY;
            if (FabriqueCarteVue.montrer)
                MainWindow.INSTANCE.montrerProp(x, y);
            else
                MainWindow.INSTANCE.cacherProp();
        }

        /**
         * \fn public void creerVille(int x, int y)
         * \brief Ajoute une ville à la carte
         * 
         * Méthode permettant d'ajouter une ville à la carte
         * 
         * \param[in] x entier représentant l'abscisse de la ville dans la carte
         * \param[in] y entier représentant l'ordonnée de la ville dans la carte
         *
         */
        public void creerVille(int x, int y)
        {
            Rectangle r = (new Rectangle());
            r.Width = FabriqueCarteVue.tailleCasePixel;
            r.Height = FabriqueCarteVue.tailleCasePixel;
            Brush b = brushFactory.get(1337);
            r.Fill = b;
            Grid.SetColumn(r, x);
            Grid.SetRow(r, 1);
            FabriqueCarteVue.addHandlers(r,y);
            FabriqueCarteVue.lignes[y].Children.Remove(MainWindow.INSTANCE.textes[x, y]);
            FabriqueCarteVue.lignes[y].Children.Add(r);
            FabriqueCarteVue.lignes[y].Children.Add(MainWindow.INSTANCE.textes[x, y]);
        }

        /**
         * \fn public void initCouleurCase(int x, int y)
         * \brief Ajoute une case de couleur à la carte
         * 
         * Méthode permettant d'ajouter une case de couleur à la carte
         * 
         * \param[in] x entier représentant l'abscisse de la case de couleur dans la carte
         * \param[in] y entier représentant l'ordonnée de la case de couleur dans la carte
         *
         */
        public void initCouleurCase(int x, int y)
        {
            Rectangle r2 = new Rectangle();
            r2.Width = FabriqueCarteVue.tailleCasePixel;
            r2.Height = FabriqueCarteVue.tailleCasePixel;
            Grid.SetColumn(r2, x);
            Grid.SetRow(r2, 1);
            FabriqueCarteVue.addHandlers(r2,y);
            FabriqueCarteVue.lignes[y].Children.Add(r2);
            couleurCase[x, y] = r2;
        }


        /**
         * \fn public void mettreAJourCase(int x, int y)
         * \brief Gère les mises à jour de l'affichage d'une case
         * 
         * Méthode permettant de gérer les mises à jour de l'affichage d'une case
         * 
         * \param[in] x entier représentant l'abscisse de la case dans la carte
         * \param[in] y entier représentant l'ordonnée de la case dans la carte
         *
         */
        public void mettreAJourCase(int x, int y)
        {

            InfosCase c = jeu.carte.getInfosCase(x, y);
            List<Unite> l = c.unites;
            TextBlock text = mainWindow.textes[x, y];
            int count = c.unites.Count;

            if (count > 0 && !brouillard[x, y])
            {
                if (text == null)
                {
                    text = new TextBlock();
                    MainWindow.INSTANCE.textes[x, y] = text;
                    text.Height = 15;
                    text.Width = 15;
                    text.Background = Brushes.White;
                    FabriqueCarteVue.addHandlers(text,y);
                    text.TextAlignment = TextAlignment.Center;
                    text.Visibility = Visibility.Hidden;
                    Grid.SetColumn(text, x);
                    Grid.SetRow(text, 1);
                    FabriqueCarteVue.lignes[y].Children.Add(text);
                    mainWindow.textes[x, y] = text;
                }
                text.Text = "" + c.unites.Count; 
                text.Foreground = c.unites[0].joueur.getBrush();
                mainWindow.textes[x, y].Visibility = System.Windows.Visibility.Visible;
            }
            else if (mainWindow.textes[x, y] !=null)
                mainWindow.textes[x, y].Visibility = System.Windows.Visibility.Hidden;
            
                if (couleurCase[x,y].Fill == null)
                    couleurCase[x, y].Fill = Brushes.Gray.Clone();
            if (brouillard[x, y])
            {
                couleurCase[x, y].Fill.Opacity = 0.5;
                couleurCase[x, y].Visibility = Visibility.Visible;
            }
            else
                couleurCase[x, y].Fill.Opacity = 0;// = Visibility.Hidden;

            if (MainWindow.INSTANCE.jeu.carte.getInfosCase(x, y).dependanceVille != null)
            {
                couleurCase[x, y].Fill = MainWindow.INSTANCE.jeu.carte.getInfosCase(x, y).dependanceVille.joueur.getBrush().Clone();
                couleurCase[x, y].Fill.Opacity = 0.2;
                couleurCase[x, y].Visibility = Visibility.Visible;
            }
        }
    }
}
